use serde::{Deserialize, Serialize};
use wither::{
    bson::{oid::ObjectId, DateTime, Document},
    Model,
};

use super::user::{SimpleUser, SimpleUserModel};

#[derive(Debug, Clone, Serialize, Deserialize, Model)]
pub struct ProfileModel {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    pub namespace: String,
    pub data: Option<Document>,
    pub history: Vec<ProfileModelDataHistory>,
    pub max_history: u8,
    pub created_at: DateTime,
    pub updated_at: DateTime,
}

impl Default for ProfileModel {
    fn default() -> Self {
        let now = DateTime::from_chrono(chrono::Utc::now());
        Self {
            id: None,
            namespace: "".to_string(),
            data: None,
            history: vec![],
            max_history: 3,
            created_at: now,
            updated_at: now,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileModelDTO {
    pub id: Option<String>,
    pub namespace: String,
    pub data: Option<Document>,
    pub history: Vec<ProfileModelDataHistoryDTO>,
    pub max_history: u8,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub updated_at: chrono::DateTime<chrono::Utc>,
}

impl From<ProfileModel> for ProfileModelDTO {
    fn from(m: ProfileModel) -> Self {
        Self {
            id: m.id.as_ref().map(ToString::to_string),
            namespace: m.namespace,
            data: m.data,
            history: m.history.into_iter().map(Into::into).collect(),
            max_history: m.max_history,
            created_at: m.created_at.to_chrono(),
            updated_at: m.updated_at.to_chrono(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileModelDataHistory {
    pub operator: SimpleUserModel,
    pub data: Option<Document>,
    pub op_time: DateTime,
}
impl_into_bson!(ProfileModelDataHistory);

impl ProfileModelDataHistory {
    pub fn new<O: Into<SimpleUserModel>, D: Into<Option<Document>>>(operator: O, data: D) -> Self {
        Self {
            operator: operator.into(),
            data: data.into(),
            op_time: DateTime::now(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileModelDataHistoryDTO {
    pub operator: SimpleUser,
    pub data: Option<Document>,
    pub op_time: chrono::DateTime<chrono::Utc>,
}

impl From<ProfileModelDataHistory> for ProfileModelDataHistoryDTO {
    fn from(h: ProfileModelDataHistory) -> Self {
        Self {
            operator: h.operator.into(),
            data: h.data,
            op_time: h.op_time.to_chrono(),
        }
    }
}
